#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <mpi.h>

#include "slave.h"

#define ILOSCWYSYLANYCHDANYCH 100
#define DLUGOSCHASLA 2

char aktualneHaslo [DLUGOSCHASLA];

char * nastepneHaslo(char * haslo, int litera)
{
	int i;
	if(haslo[litera] == 'Z')
		haslo[litera] = 'a';
	else if (haslo[litera] == 'z')
	{
		haslo[litera] = 'A';
		if(litera == 0)
			for (i = 0; i < DLUGOSCHASLA; i++)
				haslo[i] = '\0';
		else
			haslo = nastepneHaslo(haslo,litera-1);
	}
	else
		haslo[litera]++;
	return haslo;
}

int bisekcja (int start, int stop, FILE * plik, char * hash)
{
	char readHash [41];
	int poz;
	int roznica;
	roznica = stop - start;
	poz = (roznica / 2) + start;
	if (poz == -1)
	{
		poz++;
		fseek(plik, poz*(44+DLUGOSCHASLA), SEEK_SET);
		fscanf(plik, "%s", readHash);
		if (strncmp(hash, readHash, 40) == 0)
			return -1;
		else if (strncmp(hash, readHash, 40) > 0)
			return ++poz;
		else
			return poz;
	}
	
	fseek(plik, poz*(44+DLUGOSCHASLA), SEEK_SET);
	fscanf(plik, "%s", readHash);
	
	if ((stop - start) == 0)
	{
		if (strncmp(hash, readHash, 40) == 0)
			return -1;
		else
			return poz;
	}
	if (strncmp(hash, readHash, 40) < 0)
	{
		return bisekcja(start, poz, plik, hash);
	}
	else if (strncmp(hash, readHash, 40) > 0)
	{
		return bisekcja(poz+1, stop, plik, hash);
	}
	else
		return -1;
	return -1;
}

void wpisz(FILE * plik, char * haslo, char * hash)
{
	int i, iloscLinii, nrlinii;
	char ladnyhash [41];
	long size;
	char linia [44+DLUGOSCHASLA];
	fseek(plik, 0l, SEEK_END);
	size = ftell(plik);
	haslo[DLUGOSCHASLA] = '\0';
	hash[20] = '\0';
	
	for(i=0;i<20;i++)
		sprintf(ladnyhash+(2*i), "%02x", (unsigned char) hash[i]);

	if(size == 0)
	{
		fprintf(plik, "%s\t%s\r\n",ladnyhash, haslo);
	}
	else
	{
		iloscLinii = size/(44+DLUGOSCHASLA);
		nrlinii = bisekcja(0, iloscLinii, plik, ladnyhash);
		if (nrlinii != -1)
		{
			for(i = iloscLinii-1; i >= nrlinii; i--)
			{
				fseek(plik, i*(44+DLUGOSCHASLA), SEEK_SET);
				fgets(linia, 44+DLUGOSCHASLA, plik);
				fseek(plik, (i+1)*(44+DLUGOSCHASLA), SEEK_SET);
				fputs(linia, plik);
			}
			fseek(plik, nrlinii*(44+DLUGOSCHASLA), SEEK_SET);
			fprintf(plik, "%s\t%s\r\n",ladnyhash, haslo);
		}
	}
}

int main (int argc, char * argv)
{
	int i, rank, size;
	char hashe [20];
	char readHash [20];
	char linia [44+DLUGOSCHASLA];
	char hasla [DLUGOSCHASLA];
	MPI_Status recvStatus;

	for (i = 0; i < DLUGOSCHASLA; i++)
		aktualneHaslo[i] = 'A';

	MPI_Init(0,0);
	MPI_Comm_size(MPI_COMM_WORLD, &size);
	MPI_Comm_rank(MPI_COMM_WORLD, &rank);

	if (rank == 0)
	{
		FILE * plik;
		plik = fopen("rainbowTables.txt", "w+");

		while(aktualneHaslo[0] != '\0')
		{
			for (i = 1; i < size; i++)
			{
				memcpy(hasla, aktualneHaslo, DLUGOSCHASLA);
				if (aktualneHaslo != '\0')
					memcpy(aktualneHaslo, nastepneHaslo(aktualneHaslo, DLUGOSCHASLA-1),  DLUGOSCHASLA);
				MPI_Send(hasla, DLUGOSCHASLA, MPI_CHAR, i, 0, MPI_COMM_WORLD);
			}

			for (i = 1; i < size; i++)
			{
				MPI_Recv(hasla, DLUGOSCHASLA, MPI_CHAR, i, 0, MPI_COMM_WORLD, &recvStatus);
				MPI_Recv(hashe, 20, MPI_CHAR, i, 0, MPI_COMM_WORLD, &recvStatus);
				wpisz(plik, hasla, hashe);
			}
		}
		for (i = 1; i < size; i++)
		{
			MPI_Send(aktualneHaslo, DLUGOSCHASLA, MPI_CHAR, i, 0, MPI_COMM_WORLD);
		}

		fclose(plik);
	}
	else
	{
		while(1)
		{
			MPI_Recv(hasla, DLUGOSCHASLA, MPI_CHAR, 0, 0, MPI_COMM_WORLD, &recvStatus);

			memcpy(hashe, doTable(hasla), 20);

			MPI_Send(hasla, DLUGOSCHASLA, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
			MPI_Send(hashe, 20, MPI_CHAR, 0, 0, MPI_COMM_WORLD);
			if (hasla[0] == '\0')
				break;
		}
	}

	MPI_Finalize();
	return 0;
}